using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using GameState = FE.GameStateMachine<FE.Action, FE.Input>.GameState<FE.Action, FE.Input>;
using GameStateMachine = FE.GameStateMachine<FE.Action, FE.Input>;
namespace FE
{
    
    public abstract class Action
    {
        public abstract void Run(GameAdapter adapter);
    }
    public class ActionChain : Action
    {
        Queue<Action> Actions = new Queue<Action>();
        public ActionChain (params Action[] actions) {
            foreach (Action action in actions)
                Actions.Enqueue(action);
        }
        public override void Run(GameAdapter adapter)
        {
            foreach (Action action in Actions)
                action.Run(adapter);
        }
    }
    public class InitializeMapAction : Action
    {
        public Map map;
        public InitializeMapAction(Map map)
        {
            this.map = map;
        }
        public override void Run(GameAdapter adapter)
        {
            adapter.InitializeMap(map);
        }
    }
    public class PlayerTurnAction : Action
    {
        public Map map;        
        public PlayerTurnAction(Map map)
        {
            this.map = map;            
        }
        public override void Run(GameAdapter adapter)
        {
            adapter.StartPlayerTurn(map.NextPlayer());
        }
    }
    public class UnitSelectAction : Action
    {
        public int x, y;
        public override void Run(GameAdapter adapter)
        {
            UnitStatus unit = adapter.CurrentMap[x, y].unit;
            adapter.CurrentX = x;
            adapter.CurrentY = y;
            adapter.SelectUnit(unit);
            adapter.DisplayRange(adapter.CurrentNode, 0, unit.unit.MovementValue, GameAdapter.Actions.Move);
        }
    }
    public class UnitMoveAction : Action
    {
        public int x, y;
        public override void Run(GameAdapter adapter)
        {
            adapter.CurrentMap.ClearHighlights();
            adapter.CurrentUnit.hasTurn = false;
            adapter.MoveUnit(x, y);
            adapter.CurrentX = x;
            adapter.CurrentY = y;
        }
        
    }
    public class UnitAttackAction : Action
    {
        public int x, y;
        public override void Run(GameAdapter adapter)
        {
            adapter.AttackUnit(x, y);
            Map.MapNode mn = adapter.CurrentMap[x, y];
            if (mn.unit.IsDead)
            {
                mn.unit = null;
            }
            if (adapter.CurrentUnit.IsDead)
            {
                Map.MapNode mn2 = adapter.CurrentMap[adapter.CurrentX, adapter.CurrentY];
                mn2.unit = null;
            }
            adapter.ResetCurrent();
            adapter.CurrentMap.ClearHighlights();
            adapter.UpdateMap();
        }
    }
    public class Input
    {
        public override bool Equals(object obj)
        {
            return GetType() == obj.GetType();
        }
        public virtual void Initialize(Action action)
        {

        }
        public virtual bool Validate(GameAdapter adapter)
        {
            return true;
        }
    }
    public class UnitSelectInput : UnitMoveInput
    {
        public override bool Validate(GameAdapter adapter)
        {
            Map.MapNode mn = adapter.CurrentMap[X, Y];
            return mn.unit != null && mn.unit.hasTurn;
        }
        public override void Initialize(Action action)
        {
            ((UnitSelectAction)action).x = X;
            ((UnitSelectAction)action).y = Y;
        }
    }
    public class UnitChoiceAction : Action
    {
        public override void Run(GameAdapter adapter)
        {
            adapter.ActionChoice();
        }
    }
    public class UnitActionChosenInput : Input
    {
        GameAdapter.Actions action;
        public GameAdapter.Actions Action
        {
            get { return action; }
            set { action = value; }
        }
        public override bool Validate(GameAdapter adapter)
        {
            return true;
        }
        public override void Initialize(Action action)
        {
             

        }
    }
    public class UnitAttackInput : UnitMoveInput
    {
        public override bool Validate(GameAdapter adapter)
        {
            Weapon w = adapter.CurrentUnit.unit.GetEquippedWeapon();
            return w.IsInRange(new Point(adapter.CurrentX, adapter.CurrentY), new Point(X, Y));
        }
        public override void Initialize(Action action)
        {
            ((UnitAttackAction)action).x = X;
            ((UnitAttackAction)action).y = Y;            
        }
    }
    public class UnitMoveInput : Input
    {
        private int x, y;

        public UnitMoveInput this[int x, int y]
        {
            get
            {
                this.x = x;
                this.y = y;
                return this;
            }
        }

        public int X
        {
            get { return x; }
            set { x = value; }
        }
        public int Y
        {
            get { return y; }
            set { y = value; }
        }
        public override void Initialize(Action action)
        {
            ((UnitMoveAction)action).x = x;
            ((UnitMoveAction)action).y = y;
        }
        public override bool Validate(GameAdapter adapter)
        {
            List<Map.MapNode> nodes = adapter.CurrentNode.SearchMove(adapter.CurrentUnit, adapter.CurrentUnit.unit.MovementValue);
            return nodes.Contains(adapter.CurrentMap[x, y]);
        }

    }
    public class CheckTurnOverInput : Input
    {
        public override bool Validate(GameAdapter adapter)
        {
            return !adapter.CurrentPlayer.HasAnyMoves;
        }
    }
    public class EndTurnInput : Input
    {
        public override bool Validate(GameAdapter adapter)
        {
            return true;
        }
    }
    public class Game
    {
        public static RNG RNG = new RNG();

        Map map;
        GameStateMachine stateMachine;
        GameAdapter adapter;
        public Game(GameAdapter adapter)
        {
            this.adapter = adapter;
            adapter.Game = this;
            map = Map.CreateTestMap();            
            
            GameState initialState = BuildPlayerStateMachine();
            stateMachine = new GameStateMachine(initialState);
            ActionChain ac = new ActionChain(new InitializeMapAction(map), initialState.Action);
            ac.Run(adapter);
        }
        public GameState BuildPlayerStateMachine ()
        {
            GameState playerStartTurn = new GameState(new PlayerTurnAction(map));
            playerStartTurn.AddTransition(new EndTurnInput(), playerStartTurn);
            
            GameState playerSelectUnit = new GameState(new UnitSelectAction());
            playerStartTurn.AddTransition(new UnitSelectInput(), playerSelectUnit);

            GameState moveUnit = new GameState(new UnitMoveAction());
            playerSelectUnit.AddTransition(new UnitMoveInput(), moveUnit);

            GameState unitChoice = new GameState(new UnitChoiceAction());
            moveUnit.AddTransition(new UnitActionChosenInput(), unitChoice);
            
            GameState attackUnit = new GameState(new UnitAttackAction());            
            unitChoice.AddTransition(new UnitAttackInput(), attackUnit);
            unitChoice.AddTransition(new EndTurnInput(), playerStartTurn);
            unitChoice.AddTransition(new CheckTurnOverInput(), playerStartTurn);
            unitChoice.AddTransition(new UnitSelectInput(), playerSelectUnit);            
            attackUnit.AddTransition(new UnitSelectInput(), playerSelectUnit);            
            attackUnit.AddTransition(new CheckTurnOverInput(), playerStartTurn);

            return playerStartTurn;
        }
        public void Input(Input input)
        {
            GameState start = stateMachine.Current;

            if (stateMachine.HasTransition(input) && input.Validate(adapter))
            {
                stateMachine.Input(input);
                GameState end = stateMachine.Current;
                input.Initialize(stateMachine.Current.Action);
                stateMachine.Current.Action.Run(adapter);
            }
        }
    }
}
